"""
镜像版本校验服务 - 专职负责镜像版本校验和容器状态检查
"""

import json
import subprocess
import logging
from typing import Optional, Tuple, Dict
from datetime import datetime

from .types import (
    ImageInfo, ContainerInfo, VerificationResult, 
    ContainerStatus, ActionType
)


logger = logging.getLogger(__name__)


class ImageVerificationService:
    """镜像版本校验服务"""
    
    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)
    
    def verify_image_status(self, image_url: str, container_name: str) -> VerificationResult:
        """
        智能校验镜像版本和容器状态
        
        Args:
            image_url: 完整的镜像URL
            container_name: 容器名称
            
        Returns:
            VerificationResult: 校验结果
        """
        try:
            # 1. 检查本地镜像是否存在
            local_image_info = self.get_local_image_info(image_url)
            
            # 2. 检查容器状态
            container_status = self.get_container_status(container_name)
            container_info = self.get_container_info(container_name) if container_status != ContainerStatus.NOT_EXISTS else None
            
            # 3. 获取远程镜像信息（可选，用于版本对比）
            remote_image_info = None
            try:
                remote_image_info = self.get_remote_image_info(image_url)
            except Exception as e:
                self.logger.warning(f"无法获取远程镜像信息: {e}")
            
            # 4. 智能决策逻辑
            needs_action, action_type, reason = self._make_decision(
                local_image_info, container_status, remote_image_info
            )
            
            return VerificationResult(
                needs_action=needs_action,
                action_type=action_type,
                reason=reason,
                local_image_info=local_image_info,
                container_info=container_info
            )
            
        except Exception as e:
            self.logger.error(f"镜像状态校验失败: {e}")
            return VerificationResult(
                needs_action=True,
                action_type=ActionType.REBUILD,
                reason=f"校验过程发生错误，建议重建: {str(e)}"
            )
    
    def get_local_image_info(self, image_url: str) -> Optional[ImageInfo]:
        """获取本地镜像信息"""
        try:
            # 使用docker images命令获取镜像信息
            cmd = ['docker', 'images', image_url, '--format', 'json']
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
            
            if result.returncode == 0 and result.stdout.strip():
                # Docker可能返回多行JSON，取第一行
                first_line = result.stdout.strip().split('\n')[0]
                image_data = json.loads(first_line)
                
                return ImageInfo(
                    id=image_data.get('ID', ''),
                    repository=image_data.get('Repository', ''),
                    tag=image_data.get('Tag', ''),
                    created_at=image_data.get('CreatedAt', ''),
                    size=image_data.get('Size', ''),
                    digest=image_data.get('Digest')
                )
        except Exception as e:
            self.logger.debug(f"获取本地镜像信息失败: {e}")
            
        return None
    
    def get_container_status(self, container_name: str) -> ContainerStatus:
        """获取容器运行状态"""
        try:
            # 检查容器是否在运行
            cmd = ['docker', 'ps', '--filter', f'name=^{container_name}$', '--format', 'json']
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
            
            if result.returncode == 0 and result.stdout.strip():
                return ContainerStatus.RUNNING
            
            # 检查容器是否存在但停止
            cmd = ['docker', 'ps', '-a', '--filter', f'name=^{container_name}$', '--format', 'json']
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
            
            if result.returncode == 0 and result.stdout.strip():
                return ContainerStatus.STOPPED
                
            return ContainerStatus.NOT_EXISTS
            
        except Exception as e:
            self.logger.error(f"获取容器状态失败: {e}")
            return ContainerStatus.UNKNOWN
    
    def get_container_info(self, container_name: str) -> Optional[ContainerInfo]:
        """获取容器详细信息"""
        try:
            cmd = ['docker', 'ps', '-a', '--filter', f'name=^{container_name}$', '--format', 'json']
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
            
            if result.returncode == 0 and result.stdout.strip():
                first_line = result.stdout.strip().split('\n')[0]
                container_data = json.loads(first_line)
                
                return ContainerInfo(
                    id=container_data.get('ID', ''),
                    name=container_data.get('Names', ''),
                    image=container_data.get('Image', ''),
                    status=ContainerStatus(container_data.get('State', 'unknown').lower()),
                    created_at=container_data.get('CreatedAt', ''),
                    ports=self._parse_ports(container_data.get('Ports', ''))
                )
        except Exception as e:
            self.logger.debug(f"获取容器信息失败: {e}")
            
        return None
    
    def get_remote_image_info(self, image_url: str) -> Optional[ImageInfo]:
        """获取远程镜像信息（用于版本对比）"""
        try:
            # 使用docker manifest命令获取远程镜像信息
            cmd = ['docker', 'manifest', 'inspect', image_url]
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
            
            if result.returncode == 0 and result.stdout.strip():
                manifest_data = json.loads(result.stdout)
                
                return ImageInfo(
                    id='',  # 远程镜像没有本地ID
                    repository=image_url.split(':')[0],
                    tag=image_url.split(':')[1] if ':' in image_url else 'latest',
                    created_at=manifest_data.get('created', ''),
                    size='',  # manifest中没有size信息
                    digest=manifest_data.get('digest')
                )
        except Exception as e:
            self.logger.debug(f"获取远程镜像信息失败: {e}")
            
        return None
    
    def _make_decision(self, local_image: Optional[ImageInfo], 
                      container_status: ContainerStatus,
                      remote_image: Optional[ImageInfo]) -> Tuple[bool, ActionType, str]:
        """智能决策逻辑"""
        
        if not local_image:
            return True, ActionType.REBUILD, "本地镜像不存在，需要拉取并创建容器"
        
        if container_status == ContainerStatus.NOT_EXISTS:
            return True, ActionType.CREATE, "镜像存在但容器不存在，需要创建容器"
        elif container_status == ContainerStatus.STOPPED:
            if remote_image and self._is_image_outdated(local_image, remote_image):
                return True, ActionType.UPDATE, "镜像版本过旧，需要更新后重启"
            else:
                return True, ActionType.RESTART, "镜像版本正确，只需重启容器"
        elif container_status == ContainerStatus.RUNNING:
            if remote_image and self._is_image_outdated(local_image, remote_image):
                return True, ActionType.UPDATE, "容器运行中但镜像版本过旧，需要更新"
            else:
                return False, ActionType.NONE, "镜像版本正确且容器正常运行，无需操作"
        
        return False, ActionType.NONE, "状态检查完成，无需操作"
    
    def _is_image_outdated(self, local_image: ImageInfo, remote_image: ImageInfo) -> bool:
        """比对镜像版本是否过时"""
        if not local_image or not remote_image:
            return True
        
        # 如果有digest信息，优先使用digest比对
        if remote_image.digest and local_image.digest:
            return local_image.digest != remote_image.digest
        
        # 如果有创建时间，比对创建时间
        if local_image.created_at and remote_image.created_at:
            try:
                local_time = datetime.fromisoformat(local_image.created_at.replace('Z', '+00:00'))
                remote_time = datetime.fromisoformat(remote_image.created_at.replace('Z', '+00:00'))
                return remote_time > local_time
            except Exception:
                pass
        
        # 默认认为需要更新（安全起见）
        return True
    
    def _parse_ports(self, ports_str: str) -> Dict[str, str]:
        """解析端口映射字符串"""
        ports = {}
        if not ports_str:
            return ports
            
        try:
            # Docker端口格式: "0.0.0.0:8080->8080/tcp"
            for port_mapping in ports_str.split(', '):
                if '->' in port_mapping:
                    external, internal = port_mapping.split('->')
                    external_port = external.split(':')[-1]
                    internal_port = internal.split('/')[0]
                    ports[internal_port] = external_port
        except Exception as e:
            self.logger.debug(f"解析端口映射失败: {e}")
            
        return ports